package limedia.sdk.api.realtime;

import java.nio.ByteBuffer;
import java.util.concurrent.SynchronousQueue;

import com.limedia.explorer.constant.Constant;
import com.limedia.explorer.enums.Enums.TSEDecodeType;
import com.limedia.explorer.enums.Enums.TSERTPlayState;

import android.media.AudioTrack;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaCodec.CodecException;
import android.util.Log;
import android.view.Surface;

/**
 * video decoder
 * @author ThunderSoft
 *
 */
public class TSVideoDecoder {
	
	private static final String TAG = "TSVideoDecoder";
	
//	private static final int BUFFERED_FRAME_SIZE = 10;
//	private boolean isBufferFilled = false;
	
	public interface OnFrameAvailabkeListener {
        public void onFrameAvailable(long timestamp, int index);
    };
	// decoder
    public MediaCodec mCodec;
    // format
    private MediaFormat mFormat;
    private Surface mSurface = null;
    private OnFrameAvailabkeListener mFrameListener = null;
    // decoder thread
    private Thread threadDecoder = null;
    // isDecoding
    private boolean isDecoding = false;
    private int bufflength = 0;
    private SynchronousQueue<TSPcmData> mPcmDatas = null;
    private TSEDecodeType decodeType = null;
    private TSPlayerDTO mPlayerDTO = null;
    
	/**
	 * TSVideoDecoder construction
	 * @param mSurface Surface
	 * @param queue ArrayBlockingQueue<TSPcmData>
	 * @param l OnFrameAvailabkeListener
	 */
	public TSVideoDecoder(TSPlayerDTO playerDTO, Surface mSurface, SynchronousQueue<TSPcmData> queue, OnFrameAvailabkeListener l) {
		super();
		this.mSurface = mSurface;
		this.mPcmDatas = queue;
		this.mFrameListener = l;
		this.mPlayerDTO = playerDTO;
	}

	/**
	 * init decoder
	 * @param decodeType TSEDecodeType
	 */
	public TSERTPlayState initDecoder(TSEDecodeType type)
	{	
		this.decodeType = type;
		int res = -1;
		switch (this.decodeType) {
		case H264:
			res = this.init(Constant.VIDEO_DECODER_TYPE_H264);
			break;
		case H265:
			res = this.init(Constant.VIDEO_DECODER_TYPE_H265);
			break;
		}
		if(res < 0) {
			return TSERTPlayState.RT_PLAY_STATE_INIT_DEC_FAILURE;
		}
		return TSERTPlayState.RT_PLAY_STATE_SUCCESS;
	}
	
	private int init(String type) {
        try {
			this.mCodec = MediaCodec.createDecoderByType(type);
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		}
        this.mFormat = MediaFormat.createVideoFormat(
                type,
                this.mPlayerDTO.getVideoW(),
                this.mPlayerDTO.getVideoH());
        this.mFormat.setInteger(MediaFormat.KEY_FRAME_RATE,
        		this.mPlayerDTO.getVideoFrameRate());
        // configure
        this.mCodec.configure(mFormat, mSurface, null, 0);
        // start decoder
		this.mCodec.start();
        return 0;
	}
	
	/**
	 * start decode
	 */
	public void start()
	{
		Log.i(TAG, "start");
		if(!this.isDecoding) {
			this.isDecoding = true;
        	this.decode();
        	Log.i(TAG, "start end");
        }
	}
	
	/**
	 * stop decode
	 */
	public void stop()
	{
		this.isDecoding = false;
		if (null != this.threadDecoder) {
            this.threadDecoder.interrupt();
            try {
                this.threadDecoder.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            this.threadDecoder = null;
        }
		// clear Queue
		if(null != mPcmDatas) {
			mPcmDatas.clear();
		}
	}
	/**
	 * release decode
	 */
	public void release() {
		if(this.isDecoding) {
			this.stop();
		}
		// clear Queue
		if(null != mPcmDatas) {
			mPcmDatas.clear();
			mPcmDatas = null;
		}
        // stop and release decoder
        if (null != mCodec) {
            this.mCodec.stop();
            this.mCodec.release();
            this.mCodec = null;
        }
	}
	

    // strat to decode
    private void decode() {
    	Log.i(TAG, "decode start");
        // valued decoder and start mian thread
        this.threadDecoder = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    doDecode();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        threadDecoder.start();
    }
	

    /**
     * main process of decode
     */
    private void doDecode() {
    	
    	Log.i(TAG, "doDecode start");
    	
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        ByteBuffer[] inputBuffers = mCodec.getInputBuffers();
        while (this.isDecoding) {
        	
        	// TODO
//        	if(!checkBuffSize()) {
////        		Log.e(TAG, "Wait for 5 frames: " + mPcmDatas.size());
//        		continue;
//        	}
        	
        	//Log.i(TAG, "while start");
            if (this.threadDecoder.isInterrupted()) {
            	Log.i(TAG, "isInterrupted");
                break;
            }
            // Queue Input buffer
            int inputBufferIndex = mCodec.dequeueInputBuffer(10000);
            //Log.e(TAG, "inputBufferIndex: " + inputBufferIndex);
            if (inputBufferIndex >= 0) {
//                Log.e("MSG", "inputBufferIndex >= 0");

                ByteBuffer dstBuf = inputBuffers[inputBufferIndex];
                dstBuf.clear();
                TSPcmData pcmDataPop = null;
                try {
                	if(null != mPcmDatas) {
//                		Log.e("decodec", "get pcmdata before!");
                		pcmDataPop = mPcmDatas.take();
                        //Log.e("decodec", "get pcmdata success!");
                	} else {
                		Log.e(TAG, "mPcmDatas is null!");
                	}
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                int sampleSize = -1;
                if (pcmDataPop != null && pcmDataPop.getPcm() != null) {
                    sampleSize = pcmDataPop.getSize();
                }

                if (sampleSize >= 0) {
                    bufflength = pcmDataPop.getSize();
                    dstBuf.put(pcmDataPop.getPcm(), 0, bufflength);
                    // Log.e("MSG", "dstBuf.put SUCCESS!");
                }
                if (sampleSize > 0) {
                    mCodec.queueInputBuffer(inputBufferIndex, 0,
                            bufflength, 0, 0);
                }
            }
            // Queue Output buffer
            int outputBufferIndex = mCodec.dequeueOutputBuffer(info, 10000);
            //Log.e(TAG, "outputBufferIndex: " + outputBufferIndex);
            if (outputBufferIndex >= 0) {
                if (mFrameListener != null) {
                    mFrameListener.onFrameAvailable(info.presentationTimeUs, outputBufferIndex);
                }
            } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                final MediaFormat oformat = mCodec.getOutputFormat();
                getColorFormat(oformat);
                Log.e(TAG, "outputBufferIndex: MediaCodec.INFO_OUTPUT_FORMAT_CHANGED");
            } else {
            	Log.e(TAG, "outputBufferIndex: " + outputBufferIndex);
            }
        }
    }
    
//	private boolean checkBuffSize() {
//		if(mPcmDatas.isEmpty()) {
//			this.isBufferFilled = false;
//		} else if(!this.isBufferFilled && mPcmDatas.size() >= BUFFERED_FRAME_SIZE) {
//			this.isBufferFilled = true;
//		}
//		return this.isBufferFilled;
//	}

	/**
     * release OutputBuffer
     * 
     * @param index index
     */
    public void render(int index) {
        this.mCodec.releaseOutputBuffer(index, true);
    }
	
	/**
	 * get SPS and PPS
	 */
	public byte[] getSPSAndPPS()
	{
		return null;
	}
	
	/**
	 * get SPS and PPS
	 */
	public byte[] getSPSAndPPSFromRemote()
	{
		return null;
	}
	
	/**
	 * set SPS and PPS to decoder
	 */
	public void setSPSAndPPS(byte[] ppsAndSps)
	{
		
	}

	/**
	 * get decode yuv format
	 * @param format MediaFormat
	 * @return decode yuv format
	 */
	public String getColorFormat(MediaFormat format) 
	{
		int colorFormat = format.getInteger(MediaFormat.KEY_COLOR_FORMAT);

        int QOMX_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03;

        String formatString = "";
        if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format12bitRGB444) {
            formatString = "COLOR_Format12bitRGB444";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format16bitARGB1555) {
            formatString = "COLOR_Format16bitARGB1555";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format16bitARGB4444) {
            formatString = "COLOR_Format16bitARGB4444";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format16bitBGR565) {
            formatString = "COLOR_Format16bitBGR565";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format16bitRGB565) {
            formatString = "COLOR_Format16bitRGB565";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format18bitARGB1665) {
            formatString = "COLOR_Format18bitARGB1665";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format18BitBGR666) {
            formatString = "COLOR_Format18BitBGR666";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format18bitRGB666) {
            formatString = "COLOR_Format18bitRGB666";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format19bitARGB1666) {
            formatString = "COLOR_Format19bitARGB1666";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format24BitABGR6666) {
            formatString = "COLOR_Format24BitABGR6666";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format24bitARGB1887) {
            formatString = "COLOR_Format24bitARGB1887";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format24BitARGB6666) {
            formatString = "COLOR_Format24BitARGB6666";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format24bitBGR888) {
            formatString = "COLOR_Format24bitBGR888";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format24bitRGB888) {
            formatString = "COLOR_Format24bitRGB888";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format25bitARGB1888) {
            formatString = "COLOR_Format25bitARGB1888";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format32bitARGB8888) {
            formatString = "COLOR_Format32bitARGB8888";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format32bitBGRA8888) {
            formatString = "COLOR_Format32bitBGRA8888";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_Format8bitRGB332) {
            formatString = "COLOR_Format8bitRGB332";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatCbYCrY) {
            formatString = "COLOR_FormatCbYCrY";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatCrYCbY) {
            formatString = "COLOR_FormatCrYCbY";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatL16) {
            formatString = "COLOR_FormatL16";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatL2) {
            formatString = "COLOR_FormatL2";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatL24) {
            formatString = "COLOR_FormatL24";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatL32) {
            formatString = "COLOR_FormatL32";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatL4) {
            formatString = "COLOR_FormatL4";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatL8) {
            formatString = "COLOR_FormatL8";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatMonochrome) {
            formatString = "COLOR_FormatMonochrome";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatRawBayer10bit) {
            formatString = "COLOR_FormatRawBayer10bit";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatRawBayer8bit) {
            formatString = "COLOR_FormatRawBayer8bit";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatRawBayer8bitcompressed) {
            formatString = "COLOR_FormatRawBayer8bitcompressed";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYCbYCr) {
            formatString = "COLOR_FormatYCbYCr";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYCrYCb) {
            formatString = "COLOR_FormatYCrYCb";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV411PackedPlanar) {
            formatString = "COLOR_FormatYUV411PackedPlanar";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV411Planar) {
            formatString = "COLOR_FormatYUV411Planar";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar) {
            formatString = "COLOR_FormatYUV420PackedPlanar";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar) {
            formatString = "COLOR_FormatYUV420PackedSemiPlanar";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV422PackedPlanar) {
            formatString = "COLOR_FormatYUV422PackedPlanar";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV422PackedSemiPlanar) {
            formatString = "COLOR_FormatYUV422PackedSemiPlanar";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV422Planar) {
            formatString = "COLOR_FormatYUV422Planar";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV422PackedSemiPlanar) {
            formatString = "COLOR_FormatYUV422PackedSemiPlanar";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV422Planar) {
            formatString = "COLOR_FormatYUV422Planar";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV422SemiPlanar) {
            formatString = "COLOR_FormatYUV422SemiPlanar";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV444Interleaved) {
            formatString = "COLOR_FormatYUV444Interleaved";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar) {
            formatString = "COLOR_QCOM_FormatYUV420SemiPlanar";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar) {
            formatString = "COLOR_TI_FormatYUV420PackedSemiPlanar";
        } else if (colorFormat == QOMX_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka) {
            formatString = "QOMX_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka";
        } else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar) {
            formatString = "COLOR_FormatYUV420Planar";
        }

        Log.i("TAG", formatString);
		return formatString;
	}
}
